
package org.demo;

import org.apache.activemq.ActiveMQConnectionFactory;

import javax.jms.*;

/**
 * 消费者
 *
 * @author yexi
 */
public class Consumer {

    public static void main(String[] args) throws Exception {

        // 建立连接工厂。
        // 默认情况下，ActiveMQ不需要用户名和密码即可访问，除非在ActiveMQ配置文件中将 anonymousAccessAllowed 设置为 false。所以默认都是null。
        ActiveMQConnectionFactory connectionFactory = new ActiveMQConnectionFactory(
                // null
                ActiveMQConnectionFactory.DEFAULT_USER,
                // null
                ActiveMQConnectionFactory.DEFAULT_PASSWORD,
                // tcp://localhost:61616
                ActiveMQConnectionFactory.DEFAULT_BROKER_BIND_URL
        );

        // 从工厂获取一个 ActiveMQ 的连接
        Connection connection = connectionFactory.createConnection();
        System.out.println("从连接工厂获取ActiveMQ的连接: " + connection);

        // 从连接中获取会话(Session)
        Session session = connection.createSession(
                // 指定会话是否支持事务。
                false,
                // 指定消费者在确认收到消息后的确认模式，所以这个参数不会影响生产者发送消息，但是创建会话时这个参数是必须的。
                /*
                   Session.AUTO_ACKNOWLEDGE:
                   自动确认模式。
                   在这种模式下，当消费者成功消费消息时，会自动发送确认消息给ActiveMQ，告诉它该消息已被成功处理，并将其从队列中删除。
                   因此，在这种模式下，应用程序无需显式调用 acknowledge() 方法来确认消息。
                */
                /*
                    Session.CLIENT_ACKNOWLEDGE:
                    手动确认模式。
                    在这种模式下，要求消费者必须在处理完消息后显式地确认消息，即调用 acknowledge() 方法后才会确认消息。
                    如果消费者没有确认消息，那么消息会一直保持在消息队列中，直到消费者确认消息或者超时时间到期。
                    这种模式可以确保消息不会重复接收，但可能会导致消息在队列中滞留，从而影响消息系统的性能。
                    总之，这种模式的优点是可以确保消息被成功处理，但缺点是增加了应用程序的复杂性。
                 */
                /*
                   Session.DUPS_OK_ACKNOWLEDGE:
                   可重复确认模式，DUPS指的是duplicates。
                   在这种模式下，允许消费者在接收到消息后延迟确认消息，即在确认消息之前，允许消息重复接收。
                   如果消费者在处理消息时出现故障或消息被重新传送，它可以重复确认已经处理过的消息。
                   因此，这种方式可以提高消息系统的性能，因为它允许系统在不必等待确认的情况下继续发送消息，
                   而不必等待消费者确认每个消息。但是，这需要应用程序能够容忍重复消息的出现。
                   举例:
                   当处于该模式时，如果消费者确认了消息，但消息系统在传输确认消息之前断开连接，那么该消息将会被再次传送。
                   因此，在使用该模式，应用程序必须能够处理重复消息的情况，例如使用幂等性操作来确保消息处理的唯一性。
                   总之，这是一种高性能的确认模式，可以提高消息系统的吞吐量，但需要应用程序能够容忍重复消息的出现，并采取适当的措施来处理重复消息的情况。
                 */
                Session.CLIENT_ACKNOWLEDGE
        );
        System.out.println("从连接中获取会话: " + session);

        /*
            启动（或重新启动）连接的消息传递，即开始接收并传递消息。如果连接已经启动，则该方法会被忽略，不会发生任何操作。
            在调用 start() 方法之前，应用程序不能发送或接收任何消息，因为连接尚未建立。
            因此，start() 方法是确保连接处于活动状态并准备好发送和接收消息的关键步骤。
            连接的启动可以在获取会话之前或之后进行，但是必须保证在获取目的地之前启动。
         */
        connection.start();
        System.out.println("连接成功启动");

        // 从会话中获取目的地(destination)，消费者会从这个目的地取消息
        Destination queue = session.createQueue("testQueue");

        // 从会话中创建消息消费者
        MessageConsumer consumer = session.createConsumer(queue);

        for (; ; ) {

            TextMessage message = (TextMessage) consumer.receive();
            System.out.println("接收消息: " + message.getText());
            System.out.println("metadata: " + message);
            int acknowledgeMode = session.getAcknowledgeMode();
            // 如果是手动提交模式，则显式调用acknowledge()
            if (acknowledgeMode == Session.CLIENT_ACKNOWLEDGE) {
                message.acknowledge();
            }
        }

    }
}
